home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / SCSI Samples 1.0 / SCSI Driver Example 06⁄07 ƒ / disk.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-16  |  39.2 KB  |  1,373 lines  |  [TEXT/MPS ]

  1. /*******************************************************************************
  2.  
  3.     File:        disk.c 
  4.  
  5.     Contains:    
  6.         Contains most of the code for a SCSI disk driver.
  7.         This file is part of the Example SCSI Driver
  8.                         
  9.     Warnings
  10.         Copying this driver line for line and shipping it is bad
  11.         SCSI Karma.  Remember the old APDA driver?  Well this isn't
  12.         that bad, but you can do better.  This is only an example.
  13.     
  14.     Features
  15.         Supports both Original and SCSI Manager 4.3
  16.             Loads and installs with either Manager
  17.             Transitions from original to 4.3 correctly
  18.         Multiple Partition Support
  19.         New Driver Rules support
  20.         
  21.     Known Issues
  22.         There is no error handling other than retries.
  23.             No Request Sense
  24.             No bad block re-mapping
  25.         No support for older drives which require 1,511 TIBs.
  26.         The Mac Plus is not supported.
  27.         No support for hardware- or firmware-specific device
  28.         features.
  29.         
  30.         
  31.     Copyright:    © 1986-1994 by Apple Computer, Inc., all rights reserved.
  32.  
  33.         
  34. *******************************************************************************/
  35.  
  36.  
  37.  
  38. #include <types.h>
  39. #include <memory.h>
  40. #include <OSUtils.h>
  41. #include <OSEvents.h>
  42. #include <events.h>
  43. #include <files.h>
  44. #include <devices.h>
  45. #include <disks.h>
  46. #include <errors.h>
  47. #include <traps.h>
  48. /**
  49.  ** "scsi.h" in this folder is a preliminary version of the standard header.
  50.  ** When a final version is released on ETO, you should replace the following
  51.  ** #include "scsi.h" with #include <scsi.h>
  52.  **/
  53. #ifndef _SCSIAtomic
  54. #define _SCSIAtomic    0xA089
  55. #endif
  56. #include "scsi.h"
  57.  
  58. #include "disk.h"
  59. #include "DriverGestalt.h"                /* This file will eventually be rolled into devices.h */
  60.  
  61. #include "UnitNtryCnt.h"
  62.  
  63. #include "Version.r"
  64.  
  65.  
  66. /* main driver variables */
  67.  
  68.     Boolean        newSCSIPresent;            /* flag set if SCSI Manager 4.3 is present */
  69.     Boolean        forceSync;                /* force new SCSI Manager request to be synchronous */
  70.     unsigned char        numHFS;                    /* number of HFS partitions seen that need disk insert events. */
  71.     Boolean        unusedB1;
  72.  
  73. /* Per-drive variables */
  74.     SCSIInstr    tib[6];                    /* largest size of TIB needed (10 bytes/instr) */
  75.     unsigned long        scsiID;                    /* scsiID for this drive */
  76.     DeviceIdent    devIdent;                /* The device id (bus,id,lun) for this drive */
  77.     IOParam     *gioPB;                    /* Remember the current parameter block and dce for */
  78.     AuxDCE        *gdce;                    /*   the benefit of the async callback routine */
  79.     SCSI_IO        *scPB;                    /* Parameter block for async SCSI - one per drive */
  80.     unsigned long        thisTime;                /* The amount we are transferring with this transaction */
  81.     unsigned long        remaining;                /* The amount left over (bytes) if we needed to break up a transaction */
  82.     unsigned long        asyncRetries;            /* Used for retrying from a completion routine */
  83.     unsigned long        standardFlags;            /* standard set of flags for scsiFlags field */
  84.     unsigned short        standardIOFlags;        /* standard set of flags for scsiIOFlags field */
  85.     Boolean        initiateSyncData;        /* True if we need to initiate synchronous */
  86.     Boolean        unusedB2;                
  87.  
  88. /* Error Handling variables */
  89.  
  90.     unsigned char     *originalBuffer;            /* The original parameters from the prime call */
  91.     unsigned long    originalLength;                /* so we can retry the command when we are done */
  92.     unsigned long    originalBlock;                /* fixing the bad block */
  93.     short    originalDir;
  94.  
  95. /* Per-volume variables. One for every partition. */
  96. struct pervolume {
  97.     /* the DrvSts record from I.M. 4 (superset of the drive queue structure) */
  98.     
  99.     /* 
  100.     ** This whole section is a rather egregious hack.  Actually only part of the DrvQEl
  101.     ** structure is in the DrvSts structure.  As it turns out the drvQSz fields overlap
  102.     ** with the needsFlush and the diskErrs fields.  It is the same as a DrvSts2 struct
  103.     ** which doesn't appear to be used by anybody.  Fortunatly the needsFlush and diskErrs
  104.     ** fields aren't used either.  Another bogus aspect to this is the way in which it
  105.     ** is copied into the status record passed into the status call.  The code takes
  106.     ** the address of the track field and copies a #defined amount of data from there into
  107.     ** the user's buffer.  This should be done by defining this stuff as a DrvSts structure
  108.     ** and doing a structure copy or something like that.  Nevertheless it does work as is.
  109.     */
  110.     
  111.     short    track;                    /* 00: 0, unused */
  112.     char    writeProt;                /* 02: bit 7=1 if write-protected */
  113.     char    diskInPlace;            /* 03: 8, for non-ejectable disk */
  114.     char    installed;                /* 04: 1, for drive installed */
  115.     char    sides;                    /* 05: 0, unused (same as HD20) */
  116.     DrvQEl    mydqel;                    /* 06: the normal drive queue element */
  117.     /* miscellaneous per-volume data */
  118.     unsigned short    mydrvnum;                /* 16: drive number for this volume */
  119.     unsigned long    partoffset;                /* 18: phys. offset of data partition */
  120.     unsigned long    curoffset;                /* 1c: 0 = physical mapping, else 'partoffset' */
  121.     unsigned long    partblks;                /* 20: number of blocks in partition */
  122.     unsigned short    mountthispart;            /* 24: 1 if a disk insert event needs to be posted yet. */
  123.     unsigned short    unusedS;                /* 26: */
  124.     Ptr        verifyBfr;                /* 28: ptr to buffer to read into for verified writes */
  125.                                     /* 2c: */
  126. } pv[MAXPARTITIONS];
  127.  
  128. /* End of global data */
  129.  
  130.  
  131.  
  132. /* All the routines declared in this file */
  133. short    getRefNum(DeviceIdent DI);
  134. void    registerDriver(DeviceIdent DI, short refnum);
  135. void    removeDriver(DeviceIdent DI);
  136. void    NewSCSIPB(long length);
  137. void    ConfigureForBusOptions(void);
  138. short    FindAnEmptyVolumeStorage(void);
  139. unsigned char    ConvertToUC(unsigned char theCharacter);
  140. short    GetPartitionType(Partition *thePartition);
  141. OSErr    dropen(AuxDCE *dce, short mountflag, DeviceIdent DI);
  142. OSErr    drclose(AuxDCE *dce, IOParam *iopb);
  143. OSErr    prime(AuxDCE *dce, IOParam *iopb);
  144. short    BlockSCSIXfer(unsigned long wrflag, unsigned long count, unsigned long sector, unsigned char *buf, CallbackProc compRoutine);
  145. short    doNewSCSI(unsigned long wrflag, unsigned long count, unsigned long sector, unsigned char *buf, CallbackProc compRoutine);
  146. OSErr    completeIO(SCSI_IO *scPB);
  147. Ptr        getDCE(SCSI_IO *scPB);
  148. OSErr     control(AuxDCE *dce, CntrlParam *iopb);
  149. unsigned char     ByteToBCD(unsigned char c);
  150. OSErr     status(AuxDCE *dce, CntrlParam *ioPtr);
  151. OSErr     blockio(unsigned short wrflag, unsigned long count, unsigned long sector, unsigned char *buf);
  152. short     scsiop(char *cmd, unsigned short cmdlen, struct SCSIInstr *op_tib, unsigned short wr, unsigned short blind, unsigned long time);
  153. void    initcommand(unsigned char cmdbuffer[], unsigned char cmd, short len);
  154. void    Clear(char *where, short len);
  155.  
  156. Boolean    TrapAvailable(short theTrap); /* Inside Mac VI test for SCSI 4.3 trap */
  157.  
  158. /* External routines */
  159. extern char *A5Alloc(void);
  160. extern char *A5Setup(char *newA5);
  161. extern    pascal void drvrCompIO( SCSI_IO * );
  162.  
  163.  
  164.  
  165.  
  166. /* getRefNum -
  167. ** Decides what our reference number should be in the new (SCSI 4.3) environment  (called from disk.a)
  168. **
  169. ** The installation code sets bit #29 in the DeviceIdent if it was entered through the async SCSI Entry
  170. ** point.  This code uses that bit to decide what our reference number should be.
  171. **
  172. ** If the device is visible to SCSISelect we use the fixed unit table entry
  173. ** otherwise we walk the unit table to find an empty spot.  This is to allow
  174. ** older SCSI utilites like SCSI Probe to keep working.  If we didn't use the
  175. ** fixed unit table entry SCSI Probe might think there was no driver loaded and
  176. ** try to load one.
  177. */
  178. short
  179. getRefNum( DeviceIdent DI)
  180. {
  181.     unsigned long    count;
  182.     short    refnum;
  183.     unsigned char    flags;
  184.     SCSIGetVirtualIDInfoPB dIdent;
  185.     unsigned short    myUnitNtryCnt = LMGetUnitEntryCount();
  186.  
  187.     flags = DI.diReserved;
  188.     DI.diReserved = 0;        /* no longer want the extra bits in the DeviceIdent */
  189.     
  190.     if ( !(flags & 0x20) )
  191.         return( IDTOREFNUM(*(long *)&DI) );    /* Old style driver - return old-slot dRefNum */
  192.     
  193.     /* Find out if this is an old call bus and if we are going to */
  194.     /* use a static slot in the unit table */
  195.     
  196.     Clear((char *)&dIdent,sizeof(dIdent));
  197.     dIdent.scsiOldCallID = DI.targetID;
  198.     dIdent.scsiFunctionCode = SCSIGetVirtualIDInfo;
  199.     dIdent.scsiCompletion = nil;
  200.     dIdent.scsiPBLength = sizeof(SCSIGetVirtualIDInfoPB);
  201.     SCSIAction((SCSI_PB *) &dIdent);
  202.     if( dIdent.scsiExists && DI.bus == dIdent.scsiDevice.bus )        
  203.         return(IDTOREFNUM(DI.targetID));    /* Use the static slot in the unit table */
  204.     
  205.     for( count = 48; count < myUnitNtryCnt ; count++ ) {
  206.         refnum = ~count;
  207.         if( GetDCtlEntry(refnum) == 0 )
  208.             return(refnum);    /* Use the first empty spot we find */
  209.     }
  210.     
  211.     /* If we got through the Unit Table without finding an empty slot, we must
  212.         expand it.  There is sample code available from DTS showing how to do this */
  213.  
  214.     return(0);
  215. }
  216.  
  217. /* 
  218. ** Registers a driver with the SCSI Manager
  219. ** and fills in the AuxDCE info for Startup Disk
  220. **
  221. ** This only happens if we are running with the 4.3 interface.
  222. */ 
  223. void
  224. registerDriver( DeviceIdent DI, short refnum )
  225. {
  226.     SCSIDriverPB        myPB;
  227.     SCSIBusInquiryPB    inqPB;    /* Info about the scsi bus */
  228.     PackedDevID            PDevID;
  229.     
  230.     /* 
  231.     ** First we need to do a bus inquiry and determine the slot and srsrc 
  232.     ** number for the SIM we are using. We take these values and plug them
  233.     ** into our extended DCE field where startup disk can find them.  We
  234.     ** also fill in an encoded (8 bits total) version of our ID and LUN.
  235.     */
  236.     Clear((char *)&inqPB,sizeof(inqPB));
  237.     inqPB.scsiDevice = DI;
  238.     inqPB.scsiFlags = 0;
  239.     inqPB.scsiFunctionCode = SCSIBusInquiry;
  240.     inqPB.scsiCompletion = nil;
  241.     inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
  242.     SCSIAction( (SCSI_PB *) &inqPB );    /* Get slot and srsrc info for this bus */
  243.     
  244.     gdce->dCtlSlot = inqPB.scsiHBAslotNumber;
  245.     gdce->dCtlSlotId = inqPB.scsiSIMsRsrcID;
  246.     PDevID.targetID = DI.targetID;
  247.     PDevID.LUN = DI.LUN;
  248.     gdce->dCtlExtDev = *(char *)& PDevID;
  249.     
  250.     /* And the other thing we need to do is tell the SCSI Manager we are 
  251.     ** here so that other applications can find us again without relying
  252.     ** on the fixed unit table entries
  253.     */
  254.     Clear((char *)&myPB,sizeof(myPB));
  255.     myPB.scsiDevice = DI;
  256.     myPB.scsiDriver = refnum;
  257.     myPB.scsiFunctionCode = SCSICreateRefNumXref;
  258.     myPB.scsiCompletion = nil;
  259.     myPB.scsiPBLength = sizeof(SCSIDriverPB);
  260.     SCSIAction((SCSI_PB *)&myPB);
  261. }
  262.  
  263. /* 
  264. ** De-Registers a driver with the SCSI Manager
  265. **
  266. ** This function merely calls RemoveRefNumXref to let the SCSI Manager
  267. ** know we aren't controlling this Device ID anymore.
  268. */ 
  269. void
  270. removeDriver( DeviceIdent DI )
  271. {
  272.     SCSIDriverPB myPB;
  273.     
  274.     if( !newSCSIPresent )
  275.         return;
  276.         
  277.     Clear((char *)&myPB,sizeof(myPB));
  278.     myPB.scsiDevice = DI;
  279.     myPB.scsiFunctionCode = SCSIRemoveRefNumXref;
  280.     myPB.scsiCompletion = nil;
  281.     myPB.scsiPBLength = sizeof(SCSIDriverPB);
  282.     SCSIAction((SCSI_PB *)&myPB);
  283. }
  284.  
  285. /* 
  286. ** This function just fills in those parameter block values which don't
  287. ** change from call to call.  This saves a little time on each transaction.
  288. */
  289. void
  290. NewSCSIPB( long length )
  291. {
  292.     scPB->scsiPBLength        = length;
  293.     scPB->scsiDevice        = devIdent;
  294.     scPB->scsiFunctionCode    = SCSIExecIO;
  295.     scPB->scsiDataType        = scsiDataBuffer;
  296. }
  297.  
  298. /*
  299. ** This code normally gets executed twice.  Once on initialization and once when
  300. ** the first accRun tick occurs.  It sets up a the standard and IO flags to support
  301. ** whatever features of the SIM interest us. (like Fast Synchronous).  It also would
  302. ** normally be the place where features which are known not to work with a particular
  303. ** drive would be stripped out.
  304. */
  305. void
  306. ConfigureForBusOptions(void)
  307. {
  308.     SCSIBusInquiryPB    inqPB;
  309.  
  310.     Clear((char *) &inqPB, sizeof(inqPB));
  311.     inqPB.scsiDevice = devIdent;
  312.     inqPB.scsiFlags = 0;
  313.     inqPB.scsiCompletion = nil;
  314.     inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
  315.  
  316.     inqPB.scsiFunctionCode = SCSIBusInquiry;    SCSIAction( (SCSI_PB *) &inqPB );
  317.  
  318. /* Defaults */
  319.  
  320.     standardFlags    = 0;
  321.     standardIOFlags    = 0;
  322.     initiateSyncData = false;
  323.  
  324. /* 
  325. ** Check to see if sync data is supported and, if so, whether we want to use it or not
  326. ** Note that we will only try and use FAST synchronous for this driver.  IMHO synchronous
  327. ** isn't worth the effort unless you are going Fast
  328. */
  329.  
  330.     if( inqPB.scsiFlagsSupported & scsiInitiateSyncData) {
  331.         if(  inqPB.scsiFeatureFlags & scsiBusFastSCSI ) {
  332.             initiateSyncData = true;
  333.             standardIOFlags |= scsiRenegotiateSense;
  334.         }
  335.     }
  336.  
  337. /* Strip away feature flags not supported on our bus */
  338.  
  339.     standardFlags    &= inqPB.scsiFlagsSupported;
  340.     standardIOFlags    &= inqPB.scsiIOFlagsSupported;
  341.     
  342.     standardFlags |= scsiDisableAutosense;    /* This driver doesn't look at sense data */
  343. }
  344.  
  345.  
  346. /*
  347. ** FindAnEmptyVolumeStorage - Routine that searches through the pv array for a free slot.
  348. **
  349. ** Returns - if there's a free slot, the index.
  350. **           if there's no free slot, a -1.
  351. */
  352.  
  353. short
  354. FindAnEmptyVolumeStorage(void)
  355. {
  356.     short i;
  357.     
  358.     for( i = 0; i < MAXPARTITIONS; i++)
  359.         if( (pv[i].mydrvnum == (unsigned short) NODRVNUM) )
  360.             return (i);
  361.     /* All of the slots are taken. */
  362.     return (-1);
  363. }
  364.  
  365.  
  366. /*
  367. ** ConvertToUC - Converts lower case characters to upper case characters.
  368. */
  369.  
  370. unsigned char ConvertToUC (unsigned char theCharacter)
  371. {
  372.     if( (theCharacter >= 'a') && (theCharacter <= 'z'))
  373.         return (theCharacter & 0xdf);
  374.     else
  375.         return (theCharacter);
  376. }
  377.  
  378.  
  379. /*
  380. ** GetPartitionType - Used to determine what type of partition was gotten.
  381. **
  382. ** Input - thePartition, a pointer to a partition.
  383. **
  384. ** Returns - ISMACDRIVER when partition is of type Apple_Driver43 for the Macintosh.
  385. **           ISHFS when partition is of type Apple_HFS.
  386. **           ISNOTADDTOQUEUE when partition is of types Apple_partition_map, Apple_Free or Apple_Scratch.
  387. */
  388. short
  389. GetPartitionType (Partition *thePartition)
  390. {
  391.     short i,j;
  392.     static char macintoshPartitionName[] = "MACINTOSH";
  393.     char *knownPartitionTypePtr;
  394.     static  char *knownPartitionTypes[] = {
  395.         "APPLE_DRIVER43",
  396.         "APPLE_HFS",
  397.         "APPLE_PARTITION_MAP",
  398.         "APPLE_FREE",
  399.         "APPLE_SCRATCH",
  400.     };
  401.  
  402.     /* First check to see if we recognize one of the known partitions. */
  403.     for( j = POS_BEGIN; j <= POS_END; j++) {
  404.         i = 0;
  405.         knownPartitionTypePtr = knownPartitionTypes[j];
  406.         while (knownPartitionTypePtr[i] == ConvertToUC (thePartition->pmParType[i])) {
  407.             if( knownPartitionTypePtr[i++] == '\0') {
  408.                 /* We found one that we recognize. */
  409.                 switch (j) {
  410.                 case POS_APPLE_HFS:
  411.                     return (ISHFS);
  412.                 case POS_APPLE_DRIVER:
  413.                     return (ISMACDRIVER);
  414.                 default:
  415.                     /* We must have found one of the ones that can't be mounted. */
  416.                     return (ISNOTADDTOQUEUE);
  417.                 }
  418.             }
  419.         }
  420.     }
  421.     /* Wasn't something we recognized */
  422.     return (ISNOTADDTOQUEUE);
  423. }
  424.  
  425. /*
  426. ** The open routine...  (Gets called from disk.a.)
  427. **
  428. ** This routine sets up the A5 world for the driver, scans for partitions,
  429. ** installs drive queue elements and decides which SCSI Manager to use.
  430. */
  431. OSErr
  432. dropen(AuxDCE *dce, short mountflag, DeviceIdent DI)
  433. {
  434.     register struct pervolume * pvptr;
  435.     char        *mya5, *olda5;
  436.     THz            curzone;
  437.     unsigned short        dn, id;
  438.     Boolean        founddrvr;
  439.     short        err;
  440.     unsigned long        i, lastentry;
  441.     DrvQEl        *qel;
  442.     short        partitionType;
  443.     short        volumeStorageIndex;
  444.     SCSIBusInquiryPB        inqPB;    /* Info about the scsi bus */
  445.     SCSIGetVirtualIDInfoPB    dIdent;    
  446.     union {
  447.         Partition pt;
  448.         char data[BLKSIZE];
  449.     } buf;
  450.     
  451.     
  452.     if( dce->dCtlStorage != nil ) {    /* if we already have a global A5 world */
  453.         olda5 = A5Setup((char *)dce->dCtlStorage);
  454.     } else {                                 /* no A5 globals, so set them up */
  455.         curzone = GetZone();
  456.         SetZone(SystemZone());                /* globals go in the system heap */
  457.         mya5 = A5Alloc();
  458.         SetZone(curzone);
  459.         if( mya5 == NULL)                    /* can't allocate space for globals */
  460.             return(-1);
  461.         A5Init(mya5);
  462.         dce->dCtlStorage = (Handle) mya5;
  463.         olda5 = A5Setup(mya5);
  464.  
  465.         numHFS = 0;     /* No HFS partitions have been seen yet. */
  466.         
  467.         /* Show that all of the per-volume variables are currently available. */
  468.         for( i = 0; i < MAXPARTITIONS; i++) {
  469.             pv[i].mydrvnum = NODRVNUM;
  470.         }
  471.     }
  472.  
  473.     gdce = dce;        /* Remember the dce for callback routine */
  474.     
  475.     dce->dCtlFlags |= DriverGestalt_Enable_Mask;    /* Flag clients that know about Driver Gestalt */
  476.     
  477.     newSCSIPresent = false;
  478.     scPB = nil;
  479.     
  480.     if( !(*(unsigned long *)&DI & 0x20000000) ) {    /* Old style call to driver (offset 0) */
  481.         *(unsigned long *)&devIdent = -1;
  482.         scsiID = id = (*(unsigned long *)&DI) & 0xFF;
  483.     }
  484.     else {                                    /* New style call to driver (offset 8) */
  485.         devIdent = DI;
  486.         devIdent.diReserved = 0;
  487.         scsiID = id = DI.targetID;
  488.     }
  489.  
  490. /* If we have the new SCSI Mgr then see if we can use it and if so, prepare to do so */
  491.  
  492.     if(TrapAvailable(SCSIATOM)) {            
  493.         /* The trap is present and safe to use */
  494.  
  495.     /* if we were called with a SCSI ID only, get a DeviceIdent from GetVirtualIDInfo */
  496.          
  497.         if( !(*(unsigned long *)&DI & 0x20000000) ) {    /* Old style call to driver */
  498.             Clear((char *)&dIdent,sizeof(dIdent));
  499.             dIdent.scsiOldCallID = (*(unsigned long *)&DI) & 0xFF;
  500.             dIdent.scsiFunctionCode = SCSIGetVirtualIDInfo;
  501.             dIdent.scsiCompletion = nil;
  502.             dIdent.scsiPBLength = sizeof(SCSIGetVirtualIDInfoPB);
  503.             SCSIAction((SCSI_PB *) &dIdent);
  504.             if( dIdent.scsiExists )
  505.                 devIdent = dIdent.scsiDevice;
  506.         }
  507.         
  508.     /* If our device exists on a new-API capable bus, alloc a PB and get ready for new-API */
  509.         
  510.         if( *(long *)&devIdent != -1 ) {
  511.         
  512.             Clear((char *)&inqPB,sizeof(inqPB));
  513.             inqPB.scsiDevice = devIdent;
  514.             inqPB.scsiFlags = 0;
  515.             inqPB.scsiFunctionCode = SCSIBusInquiry;
  516.             inqPB.scsiCompletion = nil;
  517.             inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
  518.             SCSIAction( (SCSI_PB *) &inqPB );    /* Get the size of a SCSI_IO for this bus */
  519.             
  520.             if( inqPB.scsiResult == noErr && inqPB.scsiVersionNumber == scsiVERSION ) {
  521.                 
  522.                 if( (scPB = (SCSI_IO *)NewPtrSysClear(inqPB.scsiIOpbSize)) != 0 ) {
  523.                     newSCSIPresent = true;
  524.                     registerDriver( devIdent, dce->dCtlRefNum );
  525.                     NewSCSIPB( inqPB.scsiIOpbSize);
  526.                 }
  527.             }
  528.         }
  529.     }
  530.     if( newSCSIPresent == false ) {
  531.         *(unsigned long *)&devIdent = -1;    /* Guaranteed to NOT be a devIdent - the real one is generated in the prime
  532.                                        routine */
  533.         tib[0].scOpcode = scInc;    /* Build the re-usable parts of the TIB */
  534.         tib[0].scParam2 = BLKSIZE;
  535.         tib[1].scOpcode = scLoop;
  536.         tib[1].scParam1 = -1*sizeof(struct SCSIInstr);
  537.         tib[2].scOpcode = scStop;
  538.     }
  539.  
  540.     founddrvr = false;
  541.     lastentry = 1;
  542.  
  543.     /*
  544.     ** Walk through the partition map and look for mountable partitions.
  545.     ** 
  546.     ** If we had different devices we needed to support we could store
  547.     ** device specific information in the driver partition map entry
  548.     ** and setup the standard and IO flags and other globals accordingly.
  549.     */
  550.     for( i = 1; i <= lastentry; i++) {
  551.         err = BlockSCSIXfer( READ_F, 1, i, (unsigned char *)&buf.pt, nil);
  552.         if( err )
  553.             continue;
  554.         if( buf.pt.pmSig != pMapSIG)
  555.             continue;
  556.         if( i == 1)                    /* save the number of partition map entries */
  557.             lastentry = buf.pt.pmMapBlkCnt;
  558.         partitionType = GetPartitionType(&buf.pt);
  559.  
  560.         switch (partitionType) {
  561.  
  562.         case ISMACDRIVER:
  563.             /* You can use the driver partition map entry to store information about the drive such as
  564.                whether it supports synchronous, disconnect/reconnect etc.
  565.             */
  566.             founddrvr = true;        /* We found the driver partition. */
  567.             break;
  568.  
  569.         case ISHFS:
  570.             /* Check if there's any more room to handle another partition. */
  571.             volumeStorageIndex = FindAnEmptyVolumeStorage();
  572.             if( volumeStorageIndex == -1)
  573.                 break;
  574.             pvptr = &pv[volumeStorageIndex];    /* Saves lots of array indexing. */
  575.             /* Get a drive number for this drive. */
  576.             dn = 8;            /* Avoid any possible conficts with built-in drive numbers. */
  577.     searchqueue:
  578.             qel = (DrvQEl *)((struct QHdr *)DRVQHDR)->qHead;    /* start at drive queue head */
  579.             while (qel)    {
  580.                 if( dn == qel->dQDrive) {        /* This drivenum is already in use. */
  581.                     dn++;                        /* Try another number. */
  582.                     goto searchqueue;            /* Start at dq head again. */
  583.                 } else {
  584.                     qel = (DrvQEl *)qel->qLink;            /* otherwise goto next qel */
  585.                 }
  586.             }
  587.             /* set up DrvSts structure according to IM Vol. 4 */
  588.             pvptr->track = 0;
  589.             pvptr->writeProt = (buf.pt.pmPartStatus & 0x20) ? 0 : 0x80;    /* Look at the writable bit in partition status */
  590.             pvptr->diskInPlace = 8;            /* mark disk as non-ejectable */
  591.             pvptr->installed = 1;
  592.             pvptr->sides = 0;
  593.             pvptr->mydqel.qType = 1;
  594.             pvptr->mydqel.qLink = (QElemPtr)0;
  595.             pvptr->mountthispart = 0;        /* Show that this partition down not need to be mounted. */
  596.             pvptr->mydqel.dQFSID = 0;        /* FSID of 0 is HFS */
  597.             if( (partitionType == ISHFS) && mountflag) {
  598.                 numHFS++;                    /* We found an HFS partition. */
  599.                 pvptr->mountthispart = 1;    /* Show that this partition needs to be mounted. */
  600.             }
  601.             pvptr->mydqel.dQDrvSz = buf.pt.pmDataCnt;        /* low word */
  602.             pvptr->mydqel.dQDrvSz2 = buf.pt.pmDataCnt>>16;    /* high word */
  603.             pvptr->curoffset = pvptr->partoffset = buf.pt.pmPyPartStart + buf.pt.pmLgDataStart;
  604.             pvptr->partblks = buf.pt.pmDataCnt;
  605.             pvptr->mydrvnum = dn;
  606.             AddDrive(dce->dCtlRefNum, dn, &pvptr->mydqel);
  607.             if( mountflag && pvptr->mountthispart )
  608.                 PostEvent(diskEvt, dn);
  609.         break;
  610.  
  611.         case ISNOTADDTOQUEUE:
  612.             /* We can simply ignore this partition. */
  613.             break;
  614.         }
  615.     }
  616.     
  617.     if(  !founddrvr ) {    /* Can't locate proper entry for driver */
  618.         err = -2;
  619.     }
  620.     else {
  621.         err = 0;                /* able to open everything OK */
  622.         if(  newSCSIPresent  ) 
  623.             ConfigureForBusOptions();
  624.     }
  625.  
  626.     if( err) {        /* remove DrvQEls */
  627.         qel = (DrvQEl *)((struct QHdr *)DRVQHDR)->qHead;    /* start at drive queue head */
  628.         while (qel)    {
  629.             if( dce->dCtlRefNum == qel->dQRefNum) {            /* Is this drive for our driver? */
  630.                 Dequeue ((QElemPtr)qel, (QHdrPtr)DRVQHDR);
  631.             }
  632.             qel = (DrvQEl *)qel->qLink;    
  633.         }
  634.     }
  635.     
  636.     A5Restore(olda5);            /* restore the old A5 */
  637.     
  638.     return(err);
  639. }
  640.  
  641.  
  642. /*
  643. ** The close routine.
  644. **
  645. ** Pretty simple - SCSI drivers should implement this. It makes
  646. ** life a lot easier for utilites which need to remove older drivers.
  647. */
  648. OSErr
  649. drclose(AuxDCE *dce, IOParam *iopb)
  650. {
  651. #pragma unused(iopb)
  652.     struct pervolume *pvptr;
  653.     THz curzone;
  654.     short i;
  655.     
  656.     removeDriver( devIdent );    /* Tell the SCSI Manager we are going away  */
  657.     
  658.     /* Remove any of the drive queue entries associated with this drive. */
  659.     for( i = 0; i < MAXPARTITIONS; i++) {
  660.         pvptr = &pv[i];    /* Saves lots of array indexing. */
  661.         Dequeue ((QElemPtr)&(pvptr->mydqel), (QHdrPtr)DRVQHDR);
  662.         pvptr->mydrvnum = NODRVNUM; /* Free up the space in the per volume array. */
  663.         if( (pvptr->mountthispart) == 1)
  664.             numHFS--;    /* This partition wasn't mounted yet, it now never will be. */
  665.     }
  666.  
  667. //!!! I don't know if we need this stupid change zones stuff for a dispose.
  668.     curzone = GetZone();            /* set the proper zone... */
  669.     SetZone(SystemZone());
  670.     A5Dispose(dce->dCtlStorage);    /* dispose of the driver's A5 world */
  671.     SetZone(curzone);
  672.     return(0);
  673. }
  674.  
  675.  
  676. /*
  677. ** The prime routine...
  678. **
  679. ** Handles both asynchronous and synchronous requests identically. The
  680. ** Device Manager will do any necessary sync waits for synchronous requests.
  681. ** Immediate requests are done synchronously.
  682. */
  683. OSErr
  684. prime(AuxDCE *dce, IOParam *iopb)
  685. {
  686.     register struct pervolume *pvptr;
  687.     short        err, wrflag;
  688.     unsigned long        startblk, numblks;
  689.     short        i;
  690.     short        retries;
  691.     Boolean        success = 0;
  692.  
  693.     wrflag = ((unsigned char)iopb->ioTrap == 3);     /* 3 is the Write call */
  694.     
  695.     /* calculate the starting block and block count (divide by 512) */
  696.     startblk = dce->dCtlPosition >> 9;
  697.     numblks = iopb->ioReqCount >> 9;
  698.  
  699.     /* Search for the drive number and set up pvptr to speed up the array access. */
  700.     for( i = 0; i < MAXPARTITIONS; i++) {
  701.         pvptr = &pv[i];
  702.         if( pvptr->mydrvnum == iopb->ioVRefNum )
  703.             break;
  704.     }
  705.     
  706.     /* Make sure that we have a valid drive number. */
  707.     if( i == MAXPARTITIONS) {
  708.         err = nsDrvErr;
  709.     
  710.     } else if( ((iopb->ioReqCount & (BLKSIZE-1)) != 0) ||
  711.                ((pvptr->curoffset != 0) && (startblk+numblks > pvptr->partblks))) {
  712.         /* not a multiple of 512 bytes or block out of range of partition */
  713.         err = paramErr;
  714.  
  715.     } else {
  716.  
  717.         for( retries = 0,asyncRetries = 0; retries < MAXRETRIES; retries++ ) {
  718.             /* make I/O relative to the beginning of the partition */
  719.             gioPB = iopb;    /* Remember the parameter block for callback routine */
  720.             
  721.             /* Save the parameters in case we have to retry */
  722.             originalBuffer = (unsigned char *)iopb->ioBuffer;
  723.             originalLength = numblks;
  724.             originalBlock = startblk + pvptr->curoffset;
  725.             originalDir = wrflag;
  726.             
  727.         /* Don't go async (don't call IODone) if call is immed or forceSync is set */
  728.             if( (iopb->ioTrap & IMMED)    || (forceSync) )
  729.                 err = BlockSCSIXfer( wrflag, numblks, startblk + pvptr->curoffset,(unsigned char *)iopb->ioBuffer,0);
  730.             else
  731.                 err = BlockSCSIXfer( wrflag, numblks, startblk + pvptr->curoffset,(unsigned char *)iopb->ioBuffer, (CallbackProc)drvrCompIO);
  732.             
  733.             if( err == 1 )
  734.                 return(err);    /* Asynchronous Operation, no result yet */
  735.  
  736.             if( err == noErr ) {
  737.                 success = 1;
  738.                 break;
  739.             }
  740.             /* Must have an error so try again */
  741.                         
  742.         } 
  743.         if( success ) 
  744.             dce->dCtlPosition += (iopb->ioActCount = iopb->ioReqCount);
  745.         else {
  746.             /* Bummers */
  747.             err = ioErr;
  748.             iopb->ioActCount = 0;            /* report all or none */
  749.         }
  750.     }
  751.     return(err);
  752. }
  753.  
  754. /*
  755. ** This is the throttle point for the old and new interfaces
  756. */
  757. short
  758. BlockSCSIXfer( 
  759.     unsigned long             wrflag,
  760.     unsigned long             count,
  761.     unsigned long             sector,
  762.     unsigned char             *buf,
  763.     CallbackProc    compRoutine ) {
  764.     
  765.     if( newSCSIPresent )
  766.         return( doNewSCSI(wrflag,count,sector,buf,compRoutine) );
  767.     
  768.     return( blockio(wrflag,count,sector,buf) );
  769.     
  770. }
  771.  
  772. /*
  773. ** doNewSCSI performs a SCSI request using the new interface.  It
  774. ** can be either synchronous or asynchronous depending on whether
  775. ** it is passed a completion routine or not.
  776. */
  777. OSErr
  778. doNewSCSI( 
  779.     unsigned long             wrflag,
  780.     unsigned long             count,
  781.     unsigned long             sector,
  782.     unsigned char             *buf,
  783.     CallbackProc    compRoutine ) {
  784.  
  785.     unsigned char *        cmd;
  786.     
  787.     /*
  788.     ** ATTENTION! WAY, WAY IMPORTANT!
  789.     ** 
  790.     ** This routine can be asynchronous!  If we are operating asynchronously
  791.     ** we need to do additional chunks from the completion routine.  It is
  792.     ** NOT kosher to sit in a loop here and do multiple commands.  We need
  793.     ** to give up our context or we are not asynchronous.  Worse yet the
  794.     ** completion routine might re-use the parameter block and/or call
  795.     ** ioDone. The variables thisTime and remaining are globals so that
  796.     ** the completion routine can use them.
  797.     **
  798.     ** Sitting in a loop in the prime routine doing multiple transactions is
  799.     ** the number one most popular way to screw up an asynchronous driver.
  800.     **
  801.     ** If there is no completion routine then we loop until the entire transaction
  802.     ** is done.
  803.     */
  804.     for( ; ; ) {    /* Loop is only valid for synchronous requests */
  805.         thisTime = count & 0xFFFF;    /* 16 bits of count in a 10 byte command */
  806.         if( !thisTime )
  807.             thisTime = 0x10000;        /* 0 in a transfer count == 32 MB */
  808.         remaining = count - thisTime;
  809.     
  810.         cmd = (unsigned char *)&scPB->scsiCDB;
  811.             
  812.         cmd[0] = (wrflag ? 0x2a : 0x28);
  813.         cmd[1] = 0;                    /* !!! Add support for luns? */
  814.         
  815.         cmd[2] = sector>>24;
  816.         cmd[3] = sector>>16;
  817.         cmd[4] = sector>>8;
  818.         cmd[5] = sector;
  819.     
  820.         cmd[6] = 0;                    /* reserved */
  821.         
  822.         cmd[7] = count>>8;
  823.         cmd[8] = count;
  824.         cmd[9] = 0;                    /* reserved */
  825.         
  826.         /* Poll at the end of each block */
  827.         scPB->scsiHandshake[0] = 512;
  828.         scPB->scsiHandshake[1] = 0;
  829.             
  830.         scPB->scsiFlags        = standardFlags;
  831.         scPB->scsiIOFlags    = standardIOFlags;
  832.     
  833.         if( initiateSyncData) {
  834.             scPB->scsiFlags |= scsiInitiateSyncData;
  835.             initiateSyncData = false;
  836.         }
  837.         
  838.         if( wrflag )
  839.             scPB->scsiFlags |= scsiDirectionOut;
  840.         else
  841.             scPB->scsiFlags |= scsiDirectionIn;
  842.         scPB->scsiFlags |= scsiSIMQNoFreeze;
  843.         scPB->scsiDevice = devIdent;
  844.         scPB->scsiFunctionCode = SCSIExecIO;
  845.         scPB->scsiCDBLength = 10;
  846.                     
  847.         scPB->scsiDataPtr = buf;
  848.         scPB->scsiDataLength = count << 9;        /* Assumes 512 bytes/sector */
  849.         scPB->scsiDataType = scsiDataBuffer;
  850.         scPB->scsiTransferType = scsiTransferBlind;
  851.             
  852.         if( compRoutine ) {    
  853.         /*
  854.         ** Asynchronous
  855.         ** VERY IMPORTANT: We ALWAYS return from an asynchronous request!
  856.         ** Async requests ALWAYS give up their context.  We don't ever want
  857.         ** to run through the loop again!
  858.         */
  859.             scPB->scsiCompletion = compRoutine;
  860.             if( SCSIAction( (SCSI_PB *) scPB) ) 
  861.                 return(CMD_ERR);    /* Same convention as scsi_op */
  862.             else
  863.                 return(1);            /* Magic return value! - Tells assembly lang stub it shouldn't call IODone */ 
  864.         }
  865.         else {
  866.         /*
  867.         ** Synchronous
  868.         ** This section doesn't return if there was no error but instead 
  869.         ** will keep looping in the loop.
  870.         */
  871.             scPB->scsiCompletion = nil;
  872.         
  873.             SCSIAction( (SCSI_PB *) scPB);
  874.             
  875.             if( scPB->scsiSCSIstatus == 2 )
  876.                 return(CHK_CND);    /* Try to use same convention as scsi_op */
  877.                 
  878.             if( scPB->scsiResult )
  879.                 return(CMD_ERR);    /* Try to use same convention as scsi_op */
  880.             
  881.             if( !remaining )
  882.                 break;
  883.             count = remaining;
  884.             sector += thisTime;
  885.             buf += (thisTime << 9);
  886.                 
  887.         }
  888.     }
  889.     return(noErr);
  890. }
  891.  
  892.  
  893. /*
  894. ** This is the asynchronous completion routine.  It assumes that the driver private
  895. ** storage has been initialized with a "per drive" pointer from the globals area.
  896. ** This gives it access to the parameter block and dce.
  897. */
  898. OSErr
  899. completeIO( SCSI_IO *scPB )
  900. {
  901.     IOParam *iopb;
  902.     AuxDCE *dce;
  903.     
  904.     iopb = gioPB;
  905.     dce = gdce;
  906.     if( scPB->scsiResult != noErr || scPB->scsiSCSIstatus != 0 ) {
  907.     
  908.         if( asyncRetries++ > MAXRETRIES )
  909.             return( ioErr );
  910.         else {
  911.             if( doNewSCSI(originalDir,originalLength,originalBlock,originalBuffer, (CallbackProc)drvrCompIO) )
  912.                 return(ioErr);    /* Not much else we can do here */
  913.             else
  914.                 return(1);        /* Magic return value - don't call ioDone! */
  915.         }
  916.     }
  917.     
  918.     /*
  919.     ** If we didn't transfer the whole amount with the first tranasaction 
  920.     ** will keep calling doNewSCSI until we have used up the transfer count.
  921.     **
  922.     ** Note that the error handling is independent for each chunk we move.
  923.     */
  924.     if( remaining ) {
  925.         originalLength -= thisTime;
  926.         originalBlock += thisTime;
  927.         originalBuffer += (thisTime << 9);
  928.         if( doNewSCSI(originalDir,originalLength,originalBlock,originalBuffer, (CallbackProc)drvrCompIO) )
  929.             return(ioErr);    /* Not much else we can do here */
  930.         else
  931.             return(1);        /* We still aren't done yet */
  932.     }
  933.     
  934.     dce->dCtlPosition += (iopb->ioActCount = iopb->ioReqCount);
  935.     return(noErr);
  936. }
  937.  
  938. /* This function returns the dce from a SCSI parameter block */
  939. Ptr
  940. getDCE( SCSI_IO *scPB )
  941. {
  942. #pragma unused(scPB);
  943.     return( (Ptr)gdce );
  944. }
  945.     
  946.     
  947. /*
  948.  * The control routine...
  949.  */
  950.  
  951. #define verifyCode    5
  952. #define formatCode    6
  953. #define    ejectCode    7
  954. #define iconCode    21
  955. #define accRun        65
  956.  
  957.  
  958. OSErr
  959. control(AuxDCE *dce, CntrlParam *iopb)
  960. {
  961.  
  962.     OSErr        err;
  963.     short        i;
  964.     unsigned char        *itmp, *icon_name, *geticon(), *getProDOSIcon();
  965.     register struct pervolume    *pvptr;
  966.     SCSIBusInquiryPB            inqPB;    /* Info about the scsi bus */
  967.     SCSIGetVirtualIDInfoPB        dIdent;    
  968.  
  969.     err = 0;
  970.  
  971. /*
  972. ** For accRun calls, we need to reexamine the SCSIMgr configuration to see if the 
  973. **    new SCSI Manager has appeared (only if we are currently using the old SCSI Mgr).
  974. **    If we were already using the new API or if we are starting to use it now, we
  975. **    need to examine the capabilities of the bus that we're on to see what feature
  976. **    flags we can set in our SCSI_IO requests.
  977. */
  978.     if( iopb->csCode == accRun) {        /* one-time configuration */
  979.  
  980.     /* if we weren't using the new API (4.3), see if it's there now */
  981.  
  982.         if( !newSCSIPresent ) {
  983.             if(TrapAvailable(SCSIATOM)) {    /* 05/27 MM Use Inside Mac test */    
  984.                 /* The trap is present and safe to use */
  985.     
  986.                 Clear((char *) &dIdent, sizeof(dIdent));
  987.                 dIdent.scsiOldCallID = scsiID;
  988.                 dIdent.scsiFunctionCode = SCSIGetVirtualIDInfo;
  989.                 dIdent.scsiCompletion = nil;
  990.                 dIdent.scsiPBLength = sizeof(SCSIGetVirtualIDInfoPB);
  991.                 SCSIAction((SCSI_PB *) &dIdent);
  992.                 
  993.                 if( dIdent.scsiExists ) {
  994.                 
  995.                     Clear((char *) &inqPB, sizeof(inqPB));
  996.                     devIdent = dIdent.scsiDevice;
  997.                     inqPB.scsiDevice = devIdent;
  998.                     inqPB.scsiFlags = 0;
  999.                     inqPB.scsiFunctionCode = SCSIBusInquiry;
  1000.                     inqPB.scsiCompletion = nil;
  1001.                     inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
  1002.                     SCSIAction( (SCSI_PB *) &inqPB );    /* Get the size of a SCSI_IO for this bus */
  1003.                     
  1004.                     if( inqPB.scsiResult == noErr && inqPB.scsiVersionNumber == scsiVERSION ) {
  1005.                         if( (scPB = (SCSI_IO *)NewPtrSysClear(inqPB.scsiIOpbSize)) != 0 ) {
  1006.                             newSCSIPresent = true;
  1007.                             registerDriver(devIdent,~(scsiID + 32));    /* Announce ourselves */
  1008.                             NewSCSIPB( inqPB.scsiIOpbSize);
  1009.                         }
  1010.                     }
  1011.                 }
  1012.             }
  1013.         }
  1014.  
  1015.         if( newSCSIPresent)
  1016.             ConfigureForBusOptions();
  1017.  
  1018.         for( i = 0; i < MAXPARTITIONS; i++) {
  1019.             pvptr = &pv[i];
  1020.             if( (pvptr->mydrvnum != NODRVNUM) && pvptr->mountthispart )
  1021.                 PostEvent(diskEvt, pvptr->mydrvnum);    /* System may have forgotten about us */
  1022.         }
  1023.         /* finished the one-time configuration, so disable dNeedTime */
  1024.         dce->dCtlFlags &= 0x4FFF;
  1025.         goto done;
  1026.     }
  1027.  
  1028.  
  1029.  
  1030. /* For all other csCodes, we first must set up the pvptr */
  1031.  
  1032.     /* Search for the drive number and set up pvptr to speed up the array access. */
  1033.     for( i = 0; i < MAXPARTITIONS; i++) {
  1034.         pvptr = &pv[i];
  1035.         if( pvptr->mydrvnum == iopb->ioVRefNum )
  1036.             break;
  1037.     }
  1038.     
  1039.     /* Make sure that we have a valid drive number. */
  1040.     if( i == MAXPARTITIONS) {
  1041.         err = nsDrvErr;
  1042.  
  1043.     } else {
  1044.         switch(iopb->csCode) {
  1045.         
  1046.         case verifyCode:
  1047.         case formatCode:    /* just pretend it worked */
  1048.             break;
  1049.             
  1050.         case iconCode:
  1051.             itmp = geticon();
  1052.             /* little hack to show the SCSI ID and partition for this drive */
  1053.             BlockMove(&itmp, iopb->csParam, sizeof(itmp));
  1054.             
  1055.             if( newSCSIPresent ) 
  1056.             {
  1057.                 Clear((char *) &inqPB, sizeof(inqPB));
  1058.                 inqPB.scsiDevice = devIdent;
  1059.                 inqPB.scsiFlags = 0;
  1060.                 inqPB.scsiFunctionCode = SCSIBusInquiry;
  1061.                 inqPB.scsiCompletion = nil;
  1062.                 inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
  1063.                 SCSIAction( (SCSI_PB *) &inqPB );    /* Find out how many buses we have */
  1064.                 
  1065.                 if( inqPB.scsiHiBusID == 0 ) {
  1066.                     icon_name = "\pSCSI ID ?  (a)";
  1067.                     icon_name[ 9] = '0' + devIdent.targetID;
  1068.                 }
  1069.                 else {
  1070.                     icon_name = "\pSCSI ID ?, Bus ?  (a)";
  1071.                     icon_name[ 9] = '0' + devIdent.targetID % 8;
  1072.                     if( devIdent.bus > 9) {
  1073.                         icon_name[16] = '0' + devIdent.bus / 10;
  1074.                         icon_name[17] = '0' + devIdent.bus % 10;
  1075.                     }
  1076.                     else
  1077.                         icon_name[16] = '0' + devIdent.bus;
  1078.                 }
  1079.             }
  1080.             else {    /* Old API */
  1081.                 icon_name = "\pSCSI ID ?";
  1082.                 icon_name[9] = '0' + scsiID;
  1083.             }
  1084.             BlockMove(icon_name, itmp + 256, icon_name[0] + 1);    /* we reserved 32 bytes for strings!!! */
  1085.  
  1086.             break;
  1087.             
  1088.         case ejectCode:
  1089.             /*
  1090.             ** Old (System 4.1?) SFGetFiles will eject disks marked as not ejectable (like ours)
  1091.             ** If this happens we need to issue a disk insert post event to re-mount the disk.
  1092.             */
  1093.             PostEvent(diskEvt, pvptr->mydrvnum);
  1094.             /* fall through to default */
  1095.             
  1096.         default:
  1097.             err = controlErr;
  1098.             break;
  1099.         }
  1100.     }
  1101. done:
  1102.     return(err);
  1103. }
  1104.  
  1105.  
  1106. /* Byte to BCD Routine... */
  1107. unsigned char
  1108. ByteToBCD( unsigned char c )
  1109. {
  1110.     return( (((c) / 10) << 4) | ((c) % 10) );
  1111. }
  1112.  
  1113.  
  1114. /*
  1115.  * The status routine...
  1116.  */
  1117.  
  1118. #define    drvStsCode    8
  1119. #define    DRVSTSSIZE    22    /* csParam holds 22 bytes */
  1120.  
  1121. OSErr
  1122. status( AuxDCE * dce, CntrlParam * ioPtr)
  1123. {
  1124.     register struct pervolume *pvptr;
  1125.     OSErr            err = noErr;
  1126.     short            i;
  1127.     DriverGestaltSyncResponse syncResponse;
  1128.     DriverGestaltBootResponse bootResponse;
  1129.     DriverGestaltVersResponse versResponse;
  1130. #pragma unused (dce)
  1131.  
  1132.     /* Search for the drive number and set up pvptr to speed up the array access. */
  1133.     
  1134.     for( i = 0; i < MAXPARTITIONS; i++) {
  1135.         pvptr = &pv[i];
  1136.         if( pvptr->mydrvnum == ioPtr->ioVRefNum )
  1137.             break;
  1138.     }
  1139.     
  1140.     /* Make sure that we have a valid drive number. */
  1141.     if( i == MAXPARTITIONS) {
  1142.         err = nsDrvErr;
  1143.  
  1144.     } else 
  1145.     {
  1146.         switch(ioPtr->csCode) 
  1147.         {
  1148.         case drvStsCode:
  1149.             BlockMove(&pvptr->track, ioPtr->csParam, DRVSTSSIZE);
  1150.             break;
  1151.             
  1152.         case csDriverGestaltCode:
  1153.             
  1154.             switch ( ((DriverGestaltParam *)ioPtr)->driverGestaltSelector )
  1155.             {
  1156.             case driverGestaltSync:
  1157.                 if( newSCSIPresent )
  1158.                     syncResponse.behavesSynchronously = false;
  1159.                 else
  1160.                     syncResponse.behavesSynchronously = true;
  1161.                     
  1162.                 ((DriverGestaltParam *)ioPtr)->driverGestaltResponse = *(unsigned long *)&syncResponse;
  1163.                 break;
  1164.             
  1165.             case driverGestaltBoot:
  1166.                 if( newSCSIPresent ) {
  1167.                     bootResponse.extDev     = dce->dCtlExtDev;
  1168.                     bootResponse.partition     = i;
  1169.                     bootResponse.SIMSlot     = dce->dCtlSlot;
  1170.                     bootResponse.SIMsRSRC     = dce->dCtlSlotId;
  1171.                     
  1172.                     ((DriverGestaltParam *)ioPtr)->driverGestaltResponse = *(unsigned long *)&bootResponse;
  1173.                 }
  1174.                 else
  1175.                     err = statusErr;
  1176.                 
  1177.                 break;
  1178.                 
  1179.             case driverGestaltVersion:
  1180.                 versResponse.driverVersion.majorRev            = ByteToBCD(VersionMajor);
  1181.                 versResponse.driverVersion.minorAndBugRev    = ByteToBCD(VersionMinor & 0xf);
  1182.                 versResponse.driverVersion.stage            = VersionLevel;
  1183.                 versResponse.driverVersion.nonRelRev        = ByteToBCD(VersionStage);
  1184.                 
  1185.                 ((DriverGestaltParam *)ioPtr)->driverGestaltResponse = *(unsigned long *)&versResponse;
  1186.                 break;
  1187.                 
  1188.             default:
  1189.                 err = statusErr;            
  1190.                 break;
  1191.             }
  1192.             break;
  1193.  
  1194.         default:
  1195.             err = statusErr;
  1196.             break;
  1197.         }
  1198.     }
  1199.     return(err);
  1200. }
  1201.  
  1202.  
  1203.  
  1204.  
  1205. /* 
  1206.  * This routine will transfer the necessary data. It will break the
  1207.  * transfers up into smaller chunks if necessary.
  1208.  */
  1209. OSErr
  1210. blockio(unsigned short wrflag, unsigned long count, unsigned long sector, unsigned char *buf)
  1211. {
  1212.     unsigned long nb;
  1213.     OSErr err;
  1214.     unsigned char cmd[10];
  1215.  
  1216.     initcommand(cmd,wrflag ? 0x2A : 0x28,10);
  1217.  
  1218.     /*
  1219.     ** This is a strictly synchronous routine so it is kosher
  1220.     ** to sit in a loop while we do multiple commands.
  1221.     */
  1222.     while (count > 0) {
  1223.         nb = count & 0xFFFF;    /* 16 bits of count in a 10 byte command */
  1224.         if( !nb )
  1225.             nb = 0x10000;        /* 0 in a transfer count == 32 MB */
  1226.     
  1227.         tib[0].scParam1 = (unsigned long)buf;         /* scInc opcode does the rest */
  1228.         tib[1].scParam2 = nb = count;
  1229.     
  1230.         cmd[1] = 0;                                    /* !!! Add support for luns? */
  1231.         
  1232.         cmd[2] = sector>>24;
  1233.         cmd[3] = sector>>16;
  1234.         cmd[4] = sector>>8;            
  1235.         cmd[5] = sector;
  1236.     
  1237.         cmd[6] = 0;                                    /* reserved */
  1238.         cmd[9] = 0;                                    /* reserved */
  1239.                     
  1240.         cmd[7] = nb>>8;
  1241.         cmd[8] = nb;
  1242.         err = scsiop((char *)cmd, 10, tib, wrflag, true, 60*180);
  1243.         if( err )
  1244.                 return( err );
  1245.                 
  1246.         count -= nb;
  1247.         sector += nb;
  1248.         /* The TIB automagically gets updated */
  1249.     }
  1250.         
  1251.     return(0);
  1252. }
  1253.  
  1254.  
  1255. /*
  1256.  * A generic 'send command to controller' routine. cmd is command block,
  1257.  * cmdlen is the command length in bytes, id is SCSI id, tib is the
  1258.  * TIB pointer, wr is a write flag, blind is a flag
  1259.  * indicating blind or polled reads and writes, and time is the time in
  1260.  * ticks to wait for completion.
  1261.  */
  1262.  
  1263. short
  1264. scsiop(char *cmd, unsigned short cmdlen, struct SCSIInstr *op_tib, unsigned short wr, unsigned short blind, unsigned long time)
  1265. {
  1266.     short err, st, msg;
  1267.     short attempts;
  1268.     OSErr scErr;
  1269.  
  1270.     err = 0;
  1271.  
  1272.     for( attempts = 0; attempts < 4; attempts++ ) {
  1273.         if(  (scErr = SCSIGet()) != noErr ) {        /* arbitrate */
  1274.  
  1275.             while (SCSIStat() & 0x40)                /* wait for BSY to go away */
  1276.                 ;
  1277.         }
  1278.         else
  1279.             break;
  1280.     }
  1281.     if( scErr )
  1282.         return( ARB_ERR );
  1283.  
  1284.     if( SCSISelect(scsiID))            /* select */
  1285.         return(SEL_ERR);
  1286.  
  1287.     if(  SCSICmd(cmd, cmdlen) )        /* send command */
  1288.         err = CMD_ERR;
  1289.     
  1290.     if( !err && op_tib) {            /* send tib */
  1291.         if( blind) {
  1292.             err = wr ? SCSIWBlind((Ptr)op_tib) : SCSIRBlind((Ptr)op_tib);
  1293.         } else {
  1294.             err = wr ? SCSIWrite((Ptr)op_tib) : SCSIRead((Ptr)op_tib);
  1295.         }
  1296.     }
  1297.  
  1298.     if( SCSIComplete(&st, &msg, time))        /* get completion */
  1299.         return(COMPL_ERR);
  1300.  
  1301.     if( err && st != 2)                        /* report an error unless status is check condition */
  1302.         return(err);
  1303.  
  1304.     return(st);
  1305. }
  1306.  
  1307.  
  1308. /*
  1309.  *    This is a routine to initialize scsi commands.
  1310.  *    
  1311.  *    cmdbuffer    - pointer to buffer
  1312.  *    cmd            - command to load in byte 0
  1313.  *    len            - size of buffer
  1314.  *
  1315.  */
  1316. void
  1317. initcommand(unsigned char cmdbuffer[], unsigned char cmd, short len)
  1318.  
  1319. {
  1320.     short        index;
  1321.     
  1322.     for( index = (len - 1); index >= 0 ; index--)
  1323.         cmdbuffer[index] = 0;
  1324.         
  1325.     cmdbuffer[0] = cmd;
  1326. }
  1327.  
  1328. void
  1329. Clear( char * where, short len)
  1330. {
  1331.     for( ; len>0; len--) 
  1332.         *where++ = 0;
  1333.  
  1334. }
  1335.  
  1336.  
  1337. /*
  1338.  * Test for the presence of the SCSI Manager 4.3 trap using the
  1339.  * standard algorithm.
  1340.  */
  1341. /*
  1342.  * TrapAvailable (see Inside Mac VI 3-8)
  1343.  */
  1344. #define NumToolboxTraps() (                                \
  1345.         (NGetTrapAddress(_InitGraf, ToolTrap)            \
  1346.                 == NGetTrapAddress(0xAA6E, ToolTrap))    \
  1347.             ? 0x200 : 0x400                                \
  1348.     )
  1349. #define GetTrapType(theTrap) (                            \
  1350.         (((theTrap) & 0x800) != 0) ? ToolTrap : OSTrap    \
  1351.     )
  1352.  
  1353. Boolean
  1354. TrapAvailable(
  1355.         short                    theTrap
  1356.     )
  1357. {
  1358.         TrapType                trapType;
  1359.         
  1360.         trapType = GetTrapType(theTrap);
  1361.         if (trapType == ToolTrap) {
  1362.             theTrap &= 0x07FF;
  1363.             if (theTrap >= NumToolboxTraps())
  1364.                 theTrap = _Unimplemented;
  1365.         }
  1366.         return (
  1367.             NGetTrapAddress(theTrap, trapType)
  1368.             != NGetTrapAddress(_Unimplemented, ToolTrap)
  1369.         );
  1370. }
  1371.  
  1372.  
  1373.